package com.hds.hcp.examples;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.jets3t.service.Constants;
import org.jets3t.service.Jets3tProperties;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.acl.AccessControlList;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.model.S3Bucket;
import org.jets3t.service.model.S3Object;
import org.jets3t.service.security.AWSCredentials;
import org.jets3t.service.utils.ServiceUtils;
import org.jets3t.service.StorageService;

public class HS3UsingJetS3t {

	/**
	 * This class is the X509 Trust Manager for SSL connections. This is a
	 * promiscuous implementation that indicates that all SSL keys are trusted.
	 */
	private static class MyX509TrustManager implements X509TrustManager {
	    /*		//+ Make sure it does not redirect. This can happen when invalid user credentials are provided.
		//+   This is a bug that is fixed in HCP 5.0. So this will not be need for HCP 5.0 clients.
		clientParams.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
		

		 * The default X509TrustManager returned by SunX509.  We'll delegate
		 * decisions to it, and fall back to the logic in this class if the
		 * default X509TrustManager doesn't trust it.
		 */
		X509TrustManager sunJSSEX509TrustManager;

		MyX509TrustManager() {
		}

		/*
		 * Delegate to the default trust manager.
		 */
		public void checkClientTrusted(X509Certificate[] chain, String authType)
		         throws CertificateException {
		    // I am easy
		}

		/*
		 * Delegate to the default trust manager.
		 */
		public void checkServerTrusted(X509Certificate[] chain, String authType)
		              throws CertificateException {
		     // I am easy
		}

		/*		//+ Make sure it does not redirect. This can happen when invalid user credentials are provided.
		//+   This is a bug that is fixed in HCP 5.0. So this will not be need for HCP 5.0 clients.
		clientParams.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
		

		 * Merely pass this through.
		 */
		public X509Certificate[] getAcceptedIssuers() {
		    return sunJSSEX509TrustManager.getAcceptedIssuers();
		}
	}
	
	/**
	 * Initialize and return an HttpClient connection object for this program. 
	 * This will ready the connection to be able to handle either HTTP or HTTPS
	 * URL requests.
	 * 
	 * This routine will only respect the following settings in the jets3t.properties file
	 * to respect the HCP HTTP best practices:
	 * 
	 *     s3service.https-only, s3service.s3-endpoint-http-port, 
	 *     s3service.s3-endpoint-https-port, httpclient.max-connections-per-host,
	 *     httpclient.max-connections, httpclient.connection-timeout-ms
	 *     
	 * If desired to support all the properties in jets3t.properties, see the routine
	 * initHttpConnection in org.jets3t.service.utils.RestUtils. 
	 * 
	 */
	public static HttpClient initHttpClient(Jets3tProperties inProps) throws Exception
	{
		/*
		 * Construct an SSLSocketFactory that defines a dummy TrustManager that to accept
		 *   any SSL requests.  Same behavior as if the -k option was requested on the
		 *   curl command on Unix/Linux.
		 */

		// Setup a TrustManager to a private one that essentially says everything is good.
		SSLContext sslcontext = SSLContext.getInstance("TLS");
		sslcontext.init(null, new TrustManager[] {new MyX509TrustManager()}, null);
		
		// Setup the SSLSocketFactory to allow all host names
		SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslcontext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		

		/*
		 * Setup the HTTP and HTTPS Schemes where HTTP points to the generic SocketFactory,
		 *  and HTTPS points to the to the SSLSocketFactory we just created.
		 */
		SchemeRegistry schemeRegistry = new SchemeRegistry();

		// Only register the http scheme if configured as such.
		if ( ! inProps.getBoolProperty("s3service.https-only", true)) {
			schemeRegistry.register(new Scheme("http", 
					inProps.getIntProperty("s3service.s3-endpoint-http-port", 80),
					PlainSocketFactory.getSocketFactory()));
		}
		
		schemeRegistry.register(new Scheme("https",
				inProps.getIntProperty("s3service.s3-endpoint-https-port",  443),
				sslSocketFactory));
		
		/*
		 * Construct a ThreadSafeClientConnManager that uses the SchemeRegistry setup above.
		 * This will be used to create the HttpClient.
		 */
		ThreadSafeClientConnManager connectionMgr = new ThreadSafeClientConnManager(schemeRegistry);
	
		/*
		 * Setup the connection manager to use some HCP best practices:
		 *  Max Connections to 20 per node and total maximum to 200.
		 */
		connectionMgr.setDefaultMaxPerRoute(inProps.getIntProperty("httpclient.max-connections-per-host", 20));
		connectionMgr.setMaxTotal(inProps.getIntProperty("httpclient.max-connections", 200));
		
		/*
		 * Setup Client Connection parameters.
		 */
		HttpParams clientParams = new BasicHttpParams();
		
		//+ Make sure connection request does not block indefinitely. Default to 5000ms
		HttpConnectionParams.setConnectionTimeout(clientParams,
				inProps.getIntProperty("httpclient.connection-timeout-ms", 5000));
		
		/*
		 * Construct and return the HttpClient that will be used by all HTTP/HTTPS requests.
		 */
		return new DefaultHttpClient(connectionMgr, clientParams);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		String awsAccessKey = "aHMzdXNlcg==";  						// base64 of HCP user name: "hs3user"
		String awsSecretKey = "d0dfae70ecee372035ff17df7904ca65";	// md5 of HCP user password: "letmein01"

		AWSCredentials awsCredentials = 
		    new AWSCredentials(awsAccessKey, awsSecretKey);

		try {
			// Setup the Service object to perform the communication to HCP
			//  NOTE: The settings to point to HCP needs to be setup in the 
			S3Service s3Service = new RestS3Service(awsCredentials);
			
			Jets3tProperties props = s3Service.getJetS3tProperties();
			
			System.out.println("EndPoint:   " + props.getStringProperty("s3service.s3-endpoint", "** NOT SET **"));
			
			// Using our own httpClient so we can install our own SSL TrustManager. This Client will only
			//  respect some settings in the jets3t.properties file. See the source for this routine for
			//  which ones.
			s3Service.setHttpClient(initHttpClient(props));

			/**
			 * Let's do some tests!!
			 */
			String testBucketName = "test-bucket";
			
			S3Bucket[] myBuckets;
			
			// Issue a GET SERVICE request to get list of buckets.
			myBuckets = s3Service.listAllBuckets();
			System.out.println("How many buckets to I have in S3? " + myBuckets.length);

			S3Bucket testBucket = s3Service.createBucket(testBucketName);
			System.out.println("Created test bucket: " + testBucket.getName());
			
			// Add canned ACL.
			testBucket.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);

			// Do a HEAD on a BUCKET.
			int bucketStatus = s3Service.checkBucketStatus(testBucketName);
			switch (bucketStatus) {
			case StorageService.BUCKET_STATUS__MY_BUCKET:
				System.out.println("Bucket Status: MY_BUCKET");
				break;
			case StorageService.BUCKET_STATUS__DOES_NOT_EXIST:
				System.out.println("Bucket Status: DOES_NOT_EXIST");
				break;
			case StorageService.BUCKET_STATUS__ALREADY_CLAIMED:
				System.out.println("Bucket Status: ALREADY_CLAIMED");
				break;
			}

			myBuckets = s3Service.listAllBuckets();
			System.out.println("How many buckets to I have in S3? " + myBuckets.length);
			for (int i = 0; i < myBuckets.length; i++) {
				System.out.println("Bucket: " + myBuckets[i].getName() + " Owner: " + myBuckets[i].getOwner().getDisplayName());
			}
			
			S3Object myObjects[] = s3Service.listObjects(testBucketName);
			System.out.println("How many objects in bucket \"" + testBucketName + "\"? " + myObjects.length);

			//
			// Create an S3Object based on a string, with Content-Length set automatically and 
			// Content-Type set to "text/plain"  
			//
			String stringData = "Hello World!";
			S3Object stringObject = new S3Object("HelloWorld.txt", stringData);

			// Upload the data objects.
			s3Service.putObject(testBucket, stringObject);

			//
			// Create an S3Object based on a file, with Content-Length set automatically and 
			// Content-Type set based on the file's extension (using the Mimetypes utility class)
			//
			File fileData = new File("Input.file");
			S3Object fileObject = new S3Object(fileData);

			// Upload the data objects.
			s3Service.putObject(testBucket, fileObject);

			//
			// Create an object containing a greeting string as input stream data.
			//
			String greeting = "Hello World!";
			S3Object helloWorldObject = new S3Object("HelloWorld2.txt");
			ByteArrayInputStream greetingIS = new ByteArrayInputStream(greeting.getBytes());
			helloWorldObject.setDataInputStream(greetingIS);
			helloWorldObject.setContentLength(
			    greeting.getBytes(Constants.DEFAULT_ENCODING).length);
			helloWorldObject.setContentType("text/plain");

			// Upload the data objects.
			s3Service.putObject(testBucket, helloWorldObject);

			// Print details about the uploaded object.
			System.out.println("S3Object with data: " + helloWorldObject);
			
			S3Object objectWithHash = new S3Object(testBucket, "HelloWorld.txt", stringData);
			System.out.println("Hash value: " + objectWithHash.getMd5HashAsHex());

			ByteArrayInputStream dataIS = new ByteArrayInputStream(
			    "Here is my data".getBytes(Constants.DEFAULT_ENCODING));
			byte[] md5Hash = ServiceUtils.computeMD5Hash(dataIS);
			dataIS.reset();        
			                
			stringObject = new S3Object("MyData");
			stringObject.setDataInputStream(dataIS);
			stringObject.setMd5Hash(md5Hash);        
			
			// Retrieve the HEAD of the data object we created previously.
			S3Object objectDetailsOnly = s3Service.getObjectDetails(testBucket, "HelloWorld.txt", null, null, null, null);
			System.out.println("S3Object, details only: " + objectDetailsOnly);

			// Retrieve the whole data object we created previously
			S3Object objectComplete = s3Service.getObject(testBucket.getName(), "HelloWorld.txt");
			System.out.println("S3Object, complete: " + objectComplete);

			// Read the data from the object's DataInputStream using a loop, and print it out.
			System.out.println("Greeting:");

			BufferedReader reader = new BufferedReader(
			    new InputStreamReader(objectComplete.getDataInputStream()));
			String data = null;
			while ((data = reader.readLine()) != null) {
			    System.out.println(data);
			}

			
			// Now clean up everything.
			s3Service.deleteObject(testBucket, "HelloWorld.txt");
			s3Service.deleteObject(testBucket, "Input.file");
			s3Service.deleteObject(testBucket, "HelloWorld2.txt");
			
			s3Service.deleteBucket(testBucketName);

			myBuckets = s3Service.listAllBuckets();
			System.out.println("How many buckets to I have in S3? " + myBuckets.length);

		}
		catch (S3ServiceException e) {
			e.printStackTrace();
		}
		catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		catch (KeyManagementException e) {
			e.printStackTrace();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

}
